﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using XRpgLibrary.ScriptClasses;
using XRpgLibrary.CharacterClasses;

using Microsoft.Xna.Framework;

namespace XRpgLibrary.ScriptClasses
{
    public delegate object[] ScriptAction(object[] args);

    public abstract class GameScript
    {
        public string EventName { get; private set; }
        public string ScriptName { get; private set; }
        protected string SuspendedEventName { get; set; }
        protected Character Self { get; set; }

        public abstract void Execute(Character self, object trigger, ScriptContext context, GameTime gameTime);

        /// <summary>
        /// Sets the event and script names so that instances of game scripts can be compared.
        /// It's not meant to differentiate between two script objects of the same type on the 
        /// same event, it's only meant to see if the script is of the same type.
        /// 
        /// I made this function instead of just passing in the event and script name to a 
        /// constructor in order to keep the script interface simple -- I don't want to pollute 
        /// the script constructor with house keeping variables.
        /// </summary>
        /// <param name="eventName"></param>
        /// <param name="scriptName"></param>
        public void SetIdentity(string eventName, string scriptName)
        {
            EventName = eventName;
            ScriptName = scriptName;
        }

        public bool Is(EventScript eventScript)
        {
            return EventName  == eventScript.EventName
                && ScriptName == eventScript.ScriptName;
        }

        protected bool ActionQueue(params ScriptAction[] actions)
        {
            EnsureCompletion(Self);

            object[] actionArgs = null;
            foreach (ScriptAction action in actions)
            {
                actionArgs = action(actionArgs);
                if (actionArgs == null)
                    return false;
            }

            ResumeTrigger(Self);
            return true;
        }

        public override string ToString()
        {
            return EventName + ":" + ScriptName;
        }

        /// <summary>
        /// remove this script from the current event, and attach it to the 
        /// CharacterUpdate event so that it completes as long as the 
        /// character exsts
        /// </summary>
        internal void EnsureCompletion(Character thisCharacter)
        {
            if (this.EventName != "CharacterUpdate")
            {
                SuspendedEventName = this.EventName;
                thisCharacter.RemoveScriptEvent(this);
                thisCharacter.AddScriptEvent("CharacterUpdate", this);
            }
        }

        /// <summary>
        /// After a script has been detached from its normal event by 
        /// EnsureCompletion, this function resumes the previous trigger
        /// event, so the script can fire again as expected. 
        /// </summary>
        /// <param name="eventName"></param>
        internal void ResumeTrigger(Character self)
        {
            self.RemoveScriptEvent(this);
            self.AddScriptEvent(SuspendedEventName, this);
        }
    }
}
